/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../xos/head.h"
#include "socket_impl.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 

namespace xos
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static socket_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    socket_impl::socket_impl()
    {
        init_data();
    }

    socket_impl::~socket_impl()
    {
        term();
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::get_xos_item_data( ITEM_DATA *& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::init_data()
    {
        int ret = 0;
        memset( &m_local_addr, 0, sizeof( m_local_addr ) );
        memset( &m_peer_addr, 0, sizeof( m_peer_addr ) );
        m_hSocket = INVALID_SOCKET;
        return ret;
    }

    int socket_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int socket_impl::term()
    {
        int nRet = 0;
        init_data();
        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // udp server init
    // 
    int socket_impl::udp_init( const char * lpszLocalIp, uint16_t usLocalPort )
    {
        int nRet = 0;

        if( 0 == nRet )
        {
            m_hSocket = ::WSASocketW( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED );
            if( INVALID_SOCKET == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            BOOL bOptVal = FALSE;
            int bOptLen = sizeof( bOptVal );
            int ret = setsockopt( m_hSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&bOptVal, bOptLen );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            BOOL bOptVal = TRUE;
            int bOptLen = sizeof( bOptVal );
            int ret = setsockopt( m_hSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&bOptVal, bOptLen );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            struct sockaddr_storage & addr = m_local_addr;
            get_ipv6_addr( addr, lpszLocalIp );
            fill_ipv6_port( addr, usLocalPort );
            nRet = bind( m_hSocket, (const sockaddr *)&addr, sizeof( addr ) );
        }

        return nRet;
    }

    // 
    // udp send
    // 
    int socket_impl::udp_send( const char * lpBuf, int nLen, const char * lpszToIp, uint16_t usToPort )
    {
        int nRet = 0;

        int nAddrLen = sizeof( sockaddr_storage );
        struct sockaddr_storage addr;

        if( 0 == nRet )
        {
            nRet = get_ipv6_addr( addr, lpszToIp );
        }

        if( 0 == nRet )
        {
            fill_ipv6_port( addr, usToPort );
        }

        if( 0 == nRet )
        {
            nRet = sendto( m_hSocket, lpBuf, nLen, 0, (const struct sockaddr *)&addr, nAddrLen );
        }

        return nRet;
    }

    // 
    // udp recv
    // 
    int socket_impl::udp_recv( char * lpBuf, int nLen, char * lpszFromIp, uint16_t * lpusFromPort )
    {
        int nRet = 0;

        struct sockaddr_storage addr;
        memset( &addr, 0, sizeof( addr ) );
        int nAddrLen = sizeof( addr );

        nRet = recvfrom( m_hSocket, lpBuf, nLen, 0, (struct sockaddr *)&addr, &nAddrLen );

        if( -1 != nRet )
        {
            get_ip_port( addr, lpszFromIp, lpusFromPort );
        }
        else
        {
            //dwRet = WSAGetLastError();
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // tcp init
    // 
    int socket_impl::tcp_init( const char * lpszLocalIp, uint16_t usLocalPort, bool bBind )
    {
        int nRet = 0;

        if( 0 == nRet )
        {
            m_hSocket = ::WSASocketW( AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
            if( INVALID_SOCKET == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            BOOL bOptVal = FALSE;
            int bOptLen = sizeof( bOptVal );
            int ret = setsockopt( m_hSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&bOptVal, bOptLen );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            BOOL bOptVal = TRUE;
            int bOptLen = sizeof( bOptVal );
            int ret = setsockopt( m_hSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&bOptVal, bOptLen );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            nRet = set_nodelay();
        }

        if( ( 0 == nRet ) && bBind )
        {
            struct sockaddr_storage & addr = m_local_addr;
            get_ipv6_addr( addr, lpszLocalIp );
            fill_ipv6_port( addr, usLocalPort );
            nRet = bind( m_hSocket, (const sockaddr *)&addr, sizeof( addr ) );
        }

        return nRet;
    }

    // 
    // tcp accept
    // 
    int socket_impl::tcp_accept( i_socket ** ppv )
    {
        int nRet = 0;

        SOCKET hSocket = INVALID_SOCKET;
        sockaddr_storage addr;
        memset( &addr, 0, sizeof(addr) );
        int nLen = sizeof( addr );
        socket_impl::T * p = 0;

        *ppv = 0;

        if( 0 == nRet )
        {
            hSocket = accept( m_hSocket, (sockaddr *)&addr, &nLen );
            if( INVALID_SOCKET == hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            xos_stl::init_pool_item( p );
        }

        if( 0 == nRet )
        {
            memcpy( &p->get_peer_addr(), &addr, sizeof( addr ));
            p->m_hSocket = hSocket;
            p->set_nodelay();
            *ppv = p;
            p = 0;
        }

        xos_stl::release_interface( p );

        return nRet;
    }

    // 
    // tcp connect
    // 
    int socket_impl::tcp_connect( const char * lpszServerIp, uint16_t usServerPort )
    {
        int nRet = 0;

        sockaddr_storage addr;

        if( 0 == nRet )
        {
            nRet = get_ipv6_addr( addr, lpszServerIp );
        }

        if( 0 == nRet )
        {
            fill_ipv6_port( addr, usServerPort );
            tcp_init( 0, 0, true );
        }

        if( 0 == nRet )
        {
            nRet = connect( m_hSocket, (const sockaddr *)&addr, sizeof( sockaddr_storage ) );
        }

        if( -1 == nRet )
        {
            closesocket( m_hSocket );
            m_hSocket = INVALID_SOCKET;
        }
        else
        {
            set_peer_addr( addr );
        }

        return nRet;
    }

    // 
    // tcp侦听
    // 
    int socket_impl::tcp_listen( int nNum )
    {
        int nRet = 0;
        nRet = listen( m_hSocket, nNum );
        return nRet;
    }

    // 
    // tcp send
    // 
    int socket_impl::tcp_send( const char * lpBuf, int nLen )
    {
        int nRet = 0;

        nRet = send( m_hSocket, lpBuf, nLen, 0 );

        if( SOCKET_ERROR == nRet )
        {
            int error = WSAGetLastError();
            char buf[1024] = { 0 };
            sprintf_s( buf, "tcp_send error = %d", error );
            OutputDebugStringA( buf );
        }

        return nRet;
    }

    // 
    // tcp recv
    // 
    int socket_impl::tcp_recv( char * lpBuf, int nLen )
    {
        int nRet = 0;

        nRet = recv( m_hSocket, lpBuf, nLen, 0 );

        if( SOCKET_ERROR == nRet )
        {
            int error = WSAGetLastError();
            char buf[1024] = { 0 };
            sprintf_s( buf, "tcp_recv error = %d", error );
            OutputDebugStringA( buf );
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // fill addr
    int socket_impl::fill_addr( uint16_t af, sockaddr_storage & ss_addr, const char * lpszIp, uint16_t uPort )
    {
        int ret = 0;
        if( AF_INET6 == af )
        {
            ret = fill_ipv6( ss_addr, lpszIp, uPort );
        }
        else
        {
            ret = fill_ipv4( ss_addr, lpszIp, uPort );
        }
        return ret;
    }

    int socket_impl::fill_addr( sockaddr_storage & ss_addr, const char * lpszIp, uint16_t uPort )
    {
        int ret = 0;
        uint16_t af = m_local_addr.ss_family;
        ret = fill_addr( af, ss_addr, lpszIp, uPort );
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::fill_ipv6( sockaddr_storage & ss_addr, const char * lpszIp, uint16_t uPort )
    {
        int ret = 0;
        memset( &ss_addr, 0, sizeof( ss_addr ) );
        ss_addr.ss_family = AF_INET6;
        sockaddr_in6 & addr = *(sockaddr_in6 *)&ss_addr;
        addr.sin6_family = AF_INET6;
        fill_ipv6_port( ss_addr, uPort );
        fill_ipv6_ip( ss_addr, lpszIp );
        return ret;
    }

    int socket_impl::fill_ipv6_port( sockaddr_storage & ss_addr, uint16_t uPort )
    {
        int ret = 0;
        sockaddr_in6 & addr = *(sockaddr_in6 *)&ss_addr;
        addr.sin6_port = htons( uPort );
        return ret;
    }

    int socket_impl::fill_ipv6_ip( sockaddr_storage & ss_addr, const char * lpszIp )
    {
        int ret = 0;
        sockaddr_in6 & addr = *(sockaddr_in6 *)&ss_addr;
        if( invalid_addr_name( lpszIp ) )
        {
            addr.sin6_addr = in6addr_any;
        }
        else
        {
            inet_pton( AF_INET6, lpszIp, &addr.sin6_addr );
        }
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::fill_ipv4( sockaddr_storage & ss_addr, const char * lpszIp, uint16_t uPort )
    {
        int ret = 0;
        memset( &ss_addr, 0, sizeof( ss_addr ) );
        ss_addr.ss_family = AF_INET;
        sockaddr_in & addr = *(sockaddr_in *)&ss_addr;
        addr.sin_family = AF_INET;
        fill_ipv4_port( ss_addr, uPort );
        fill_ipv4_ip( ss_addr, lpszIp );
        return ret;
    }

    int socket_impl::fill_ipv4_port( sockaddr_storage & ss_addr, uint16_t uPort )
    {
        int ret = 0;
        sockaddr_in & addr = *(sockaddr_in *)&ss_addr;
        addr.sin_port = htons( uPort );
        return ret;
    }

    int socket_impl::fill_ipv4_ip( sockaddr_storage & ss_addr, const char * lpszIp )
    {
        int ret = 0;
        sockaddr_in & addr = *(sockaddr_in *)&ss_addr;
        if( invalid_addr_name( lpszIp ) )
        {
            addr.sin_addr.s_addr = htonl( INADDR_ANY );
        }
        else
        {
            inet_pton( AF_INET, lpszIp, &addr.sin_addr );
        }
        return ret;
    }

    // ipv4 map ipv6
    int socket_impl::ipv4_map_ipv6( char * lpszIpv6, int nSize, const char * lpszIpv4 )
    {
        int ret = 0;
        mgr::container()->crt()->sprintf( lpszIpv6, nSize, "::ffff:%s", lpszIpv4 );
        return ret;
    }

    int socket_impl::get_ipv6_addr( sockaddr_storage & addr, const char * lpszIp )
    {
        int ret = 0;

        struct sockaddr_storage ipv6, ipv4;

        if( 0 == ret )
        {
            get_addr_by_name( lpszIp, &ipv6, &ipv4 );
            if( AF_INET6 != ipv6.ss_family )
            {
                if( AF_INET == ipv4.ss_family )
                {
                    char ip[100] = { 0 }, mapped_ip[100] = { 0 };
                    uint16_t uPort = 0;
                    get_ip_port( ipv4, ip, &uPort );
                    ipv4_map_ipv6( mapped_ip, sizeof( mapped_ip ) / sizeof( mapped_ip[0] ), ip );
                    get_addr_by_name( mapped_ip, &ipv6, &ipv4 );
                }
                else
                {
                    ret = 1;
                }
            }
        }

        if( 0 == ret )
        {
            memcpy( &addr, &ipv6, sizeof( addr ) );
        }

        return ret;
    }

    bool socket_impl::invalid_addr_name( const char * lpszName )
    {
        bool bRet = false; 
        if( !lpszName || ( 0 == strlen( lpszName ) ) )
        {
            bRet = true;
        }
        return bRet;
    }

    // 
    // 解析域名或机器名
    // 
    int socket_impl::get_addr_by_name( const char * lpszName, sockaddr_storage * pIpv6Addr, sockaddr_storage * pIpv4Addr )
    {
        int nRet = 0;
        int ret = 0;

        struct addrinfo * result = 0;
        struct addrinfo * pIpv6 = 0;
        struct addrinfo * pIpv4 = 0;

        if( pIpv6Addr )
        {
            memset( pIpv6Addr, 0, sizeof( sockaddr_storage ) );
        }

        if( pIpv4Addr )
        {
            memset( pIpv4Addr, 0, sizeof( sockaddr_storage ) );
        }

        if( invalid_addr_name( lpszName ) )
        {
            if( pIpv6Addr )
            {
                fill_ipv6_ip( *pIpv6Addr, lpszName );
                pIpv6Addr->ss_family = AF_INET6;
            }
            if( pIpv4Addr )
            {
                fill_ipv4_ip( *pIpv4Addr, lpszName );
                pIpv4Addr->ss_family = AF_INET;
            }
            ret = 1;
        }

        if( 0 == ret )
        {
            struct addrinfo hints;
            memset( &hints, 0, sizeof( hints ) );

            hints.ai_family = AF_UNSPEC;    // Allow IPv4/IPv6
            hints.ai_flags = AI_ADDRCONFIG; // 本地支持的协议
            hints.ai_protocol = 0;
            hints.ai_socktype = 0;

            int r = getaddrinfo( lpszName, NULL, &hints, &result );
            if( -1 == r )
            {
                nRet = 1;
                ret = 1;
            }
        }

        // 寻找ipv6和ipv4地址各一个
        if( 0 == ret )
        {
            struct addrinfo * pFind = 0;
            for( pFind = result; 0 != pFind; pFind = pFind->ai_next )
            {
                if( AF_INET6 == pFind->ai_family )
                {
                    pIpv6 = pFind;
                }
                else
                {
                    pIpv4 = pFind;
                }
                if( pIpv6 && pIpv4 )
                {
                    break;
                }
            }
        }

        if( ( 0 == ret ) && pIpv6 && pIpv6Addr )
        {
            sockaddr_in6 & ret_addr = *(sockaddr_in6 *)pIpv6->ai_addr;
            pIpv6Addr->ss_family = AF_INET6;
            sockaddr_in6 & addr = *(sockaddr_in6 *)pIpv6Addr;
            memcpy( &addr, &ret_addr, sizeof( addr ) );
        }

        if( ( 0 == ret ) && pIpv4 && pIpv4Addr )
        {
            sockaddr_in & ret_addr = *(sockaddr_in *)pIpv4->ai_addr;
            pIpv4Addr->ss_family = AF_INET;
            sockaddr_in & addr = *(sockaddr_in *)pIpv4Addr;
            memcpy( &addr, &ret_addr, sizeof( addr ) );
        }

        if( result )
        {
            freeaddrinfo( result );
            result = 0;
        }

        return nRet;
    }

    // 
    // get ip and port
    // 
    int socket_impl::get_ip_port( sockaddr_storage & ss_addr, char * lpszIp, uint16_t * lpusPort )
    {
        int ret = 0;

        sockaddr_storage * res = &ss_addr;
        const void * pAddr = 0;

        uint16_t uPort = 0;
        char ip[100] = { 0 };

        if( AF_INET6 == res->ss_family )
        {
            sockaddr_in6 & addr = *(sockaddr_in6 *)res;
            pAddr = &addr.sin6_addr;
            uPort = ntohs( addr.sin6_port );
        }
        else
        {
            sockaddr_in & addr = *(sockaddr_in *)res;
            pAddr = &addr.sin_addr;
            uPort = ntohs( addr.sin_port );
        }

        inet_ntop( res->ss_family, pAddr, ip, sizeof( ip ) );

        if( lpszIp )
        {
            lstrcpyA( lpszIp, ip );
        }

        if( lpusPort )
        {
            *lpusPort = uPort;
        }

        return ret;
    }

    // 
    // 根据socket句柄得到IP和端口
    // 
    int socket_impl::get_local_addr( char * lpszIp, uint16_t * lpusPort )
    {
        int nRet = 0;

        sockaddr_storage addr;
        memset( &addr, 0, sizeof( addr ) );
        int addr_len = sizeof( addr );

        if( 0 == nRet )
        {
            int ret = getsockname( m_hSocket, (sockaddr *)&addr, &addr_len );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            nRet = get_ip_port( addr, lpszIp, lpusPort );
        }

        return nRet;
    }

    int socket_impl::get_peer_addr( char * lpszIp, uint16_t * lpusPort )
    {
        int nRet = 0;

        sockaddr_storage addr;
        memset( &addr, 0, sizeof( addr ) );
        int addr_len = sizeof( addr );

        if( 0 == nRet )
        {
            int ret = getpeername( m_hSocket, (sockaddr *)&addr, &addr_len );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            nRet = get_ip_port( addr, lpszIp, lpusPort );
        }

        return nRet;
    }

    void socket_impl::set_socket( uint64_t ulSocket )
    {
        m_hSocket = (SOCKET)ulSocket;
    }

    uint64_t socket_impl::get_socket()
    {
        return (uint64_t)m_hSocket;
    }

    int socket_impl::set_async( bool bAsync )
    {
        int nRet = 0;
        unsigned long ulValue = bAsync ? 1 : 0;
        nRet = ioctlsocket( m_hSocket, FIONBIO, &ulValue );
        return nRet;
    }

    int socket_impl::set_nodelay()
    {
        int nRet = 0;
        BOOL bOptVal = TRUE;
        int bOptLen = sizeof( bOptVal );
        nRet = setsockopt( m_hSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&bOptVal, bOptLen );
        if( SOCKET_ERROR == nRet )
        {
            nRet = 1;
        }
        return nRet;
    }

    int socket_impl::shut_down( int how )
    {
        int ret = 0;

        int sd = SD_SEND;

        switch( how )
        {
        case XOS_SD_RECEIVE:
        {
            sd = SD_RECEIVE;
        }
        break;
        case XOS_SD_SEND:
        {
            sd = SD_SEND;
        }
        break;
        case XOS_SD_BOTH:
        {
            sd = SD_BOTH;
        }
        break;
        }

        if( INVALID_SOCKET != m_hSocket )
        {
            shutdown( m_hSocket, sd );
        }

        return ret;
    }

    int socket_impl::close( int onoff, int linger )
    {
        int nRet = 0;

        if( INVALID_SOCKET == m_hSocket )
        {
            return 0;
        }
        {
            struct linger so_linger;
            memset( &so_linger, 0, sizeof( so_linger ) );
            so_linger.l_linger = linger;
            so_linger.l_onoff = onoff;
            setsockopt( m_hSocket,
                SOL_SOCKET,
                SO_LINGER,
                (const char *)&so_linger,
                sizeof( so_linger ) );
        }
        closesocket( m_hSocket );
        m_hSocket = INVALID_SOCKET;

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 保存地址
    // 
    void socket_impl::set_local_addr( sockaddr_storage & ss_addr )
    {
        memcpy( &m_local_addr, &ss_addr, sizeof( ss_addr ) );
    }

    void socket_impl::set_peer_addr( sockaddr_storage & ss_addr )
    {
        memcpy( &m_peer_addr, &ss_addr, sizeof( ss_addr ) );
    }

    sockaddr_storage & socket_impl::get_local_addr()
    {
        return m_local_addr;
    }

    sockaddr_storage & socket_impl::get_peer_addr()
    {
        return m_peer_addr;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 根据协议类型进行bind
    int socket_impl::bind_socket( uint16_t af, uint64_t hSocket, const char * lpszIp, uint16_t uPort )
    {
        int ret = 0;
        sockaddr_storage ss_addr;
        fill_addr( af, ss_addr, lpszIp, uPort );
        ret = bind( (SOCKET)hSocket, (const sockaddr *)&ss_addr, sizeof( ss_addr ) );
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 得到本机名称和地址
    // 
    int socket_impl::get_name_and_ip( char * lpszName, char * lpszIpv6, char * lpszIpv4 )
    {
        int nRet = 0;

        sockaddr_storage ipv6, ipv4;
        char name[1024] = { 0 };

        {
            memset( &ipv6, 0, sizeof( ipv6 ) );
            memset( &ipv4, 0, sizeof( ipv4 ) );
        }

        if( 0 == nRet )
        {
            nRet = gethostname( name, sizeof( name ) );
        }

        if( 0 == nRet )
        {
            nRet = get_addr_by_name( name, &ipv6, &ipv4 );
        }

        if( (AF_INET6 == ipv6.ss_family ) && lpszIpv6 )
        {
            get_ip_port( ipv6, lpszIpv6, 0 );
        }

        if( ( AF_INET == ipv4.ss_family ) && lpszIpv4 )
        {
            get_ip_port( ipv4, lpszIpv4, 0 );
        }

        if( lpszName )
        {
            lstrcpyA( lpszName, name );
        }

        return nRet;
    }


} // xos
